package com.lx.common.utils;

import cn.hutool.core.util.StrUtil;
import com.lx.constant.ano.FieldFunction;
import com.lx.constant.execption.LambdaCheckException;

import java.lang.invoke.SerializedLambda;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SerializedLambdaUtil {
    /**
     * 反序列化缓存
     */
    private static final Map<Class, WeakReference<SerializedLambda>> LAMBDA_CACHE = new ConcurrentHashMap<>(1 << 7);

    /**
     * get方法前缀
     */
    private static final String PREFIX_GET = "get";
    /**
     * is方法前缀
     */
    private static final String PREFIX_IS = "is";

    private static final String LAMBDA_WEAK_METHOD_NAME = "writeReplace";

    /**
     * 解析 lambda 表达式
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型，被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     */
    public static <T> SerializedLambda resolve(FieldFunction<T, ?> func) {
        Class<? extends FieldFunction> clazz = func.getClass();
        if (!clazz.isSynthetic()) {
            throw new LambdaCheckException("该方法仅能传入 lambda 表达式[::]产生的合成类");
        }
        SerializedLambda res;

        WeakReference<SerializedLambda> cache = LAMBDA_CACHE.get(clazz);
        if (null != cache && (res = cache.get()) != null) {
            return res;
        }
        try {
            Method method = clazz.getDeclaredMethod(LAMBDA_WEAK_METHOD_NAME);
            method.setAccessible(true);
            res = (SerializedLambda) method.invoke(func);
            LAMBDA_CACHE.put(clazz, new WeakReference<SerializedLambda>(res));
            return res;
        } catch (Exception e) {
            throw new IllegalArgumentException("解析Lambda表达式获取列名失败!", e);
        }
    }

    /**
     * 根据SerializedLambda类获取解析方法中的列名；需要严格使用JavaBean规范,仅支持get/is方法获取.
     * 注释:如不存在上述方法,则默认将首字母小写
     *
     * @param lambda
     * @return
     */
    public static String getFieldNameByLambda(SerializedLambda lambda) {
        return getFieldNameByLambda(lambda, false);
    }

    /**
     * 根据SerializedLambda类获取解析方法中的列名；需要严格使用JavaBean规范,仅支持get/is方法获取.
     * 注释:如不存在上述方法,则默认将首字母小写
     *
     * @param lambda
     * @param ignoreFirstCase 忽略首字符大小写
     * @return
     */
    public static String getFieldNameByLambda(SerializedLambda lambda, boolean ignoreFirstCase) {
        if (lambda == null) {
            return null;
        }
        String getMethodName = lambda.getImplMethodName();
        if (getMethodName.startsWith(PREFIX_GET)) {
            getMethodName = getMethodName.substring(3);
        } else if (getMethodName.startsWith(PREFIX_IS)) {
            getMethodName = getMethodName.substring(2);
        }
        // 小写第一个字母
        return ignoreFirstCase ? getMethodName : StrUtil.lowerFirst(getMethodName);
    }


    /**
     * 根据类名类获取解析方法中的列名；需要严格使用JavaBean规范,仅支持get/is方法获取.
     * 注释:如不存在上述方法,则默认将首字母小写
     *
     * @param fLambda
     * @return
     */
    public static String getColumnName(FieldFunction<?, ?> fLambda) {
        return getFieldNameByLambda(resolve(fLambda));
    }

    /**
     * 获取真实实现类
     *
     * @param serializedLambda
     * @return
     */
    public static Class<?> getImplClass(SerializedLambda serializedLambda) {
        if (serializedLambda == null) {
            return null;
        }

        String implClassName = serializedLambda.getImplClass().replace("/", ".");
        try {
            return Class.forName(implClassName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
